package com.atguigu.gulimall.search.thread;

import org.springframework.cache.annotation.Cacheable;

import java.util.concurrent.*;

/**
 * @ClassName ThreadTest
 * @Description TODO
 * @Author 13290
 * @Date 2022/11/1 10:35
 **/
public class ThreadTest {

    /**
     * 常见的4种线程池
     *      1.定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
     *          core 和 max 均为设置的值 所有都不可以回收
     *          ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
     *      2.缓存线程池: 如果线程池长度超出处理需要,可灵活回收线程,若无可回收,则新建线程
     *          core 为 0 max为默认值,Integer的最大值 所有线程都可以回收
     *          ExecutorService executorService = Executors.newCachedThreadPool();
     *      3.定时任务线程池
     *          core 为指定的值
     *          ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
     *      4.单线程池
     *          core 为1  队列很大 顺序执行
     *          ExecutorService executorService = Executors.newSingleThreadExecutor();
     *
     **/
    //定长线程池 当前系统中池 只有一两个,每一个异步任务,提交给线程池,让线程池自己执行
    public static ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        System.out.println("main-----start");
//        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果: " + i);
//        }, newFixedThreadPool);
        //whenComplete感知异常,但是不可以处理
        /**
         * 方法完成后的感知
         **/
//        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果: " + i);
//            return i;
//        }, newFixedThreadPool).whenComplete((result,exception) -> {
//            //虽然能得到异常信息,但是没法修改返回数据  只是一个监控器 只能监控不能调整
//            System.out.println("异步任务成功完成----------结果是" + result + "异常是" + exception);
//        }).exceptionally(throwable -> {
//            //可以感知异常,同时返回默认值
//            return 10;
//        });
        /**
         * 方法执行完成后的处理
         **/
//        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果: " + i);
//            return i;
//        }, newFixedThreadPool).handle((res,thr) -> {
//            if (res != null){
//                return res * 2;
//            }
//            if (thr != null){
//                return 0;
//            }
//            return 0;
//        });
        /**
         * 线程串行化
         * 1.thenRun: 不能获取到上一步的执行结果,无返回值
         *  .thenRunAsync(() -> {
         *             System.out.println("任务2启动了-----");
         *         },newFixedThreadPool);
         * 2.thenAccept: 能接收上一步的结果,但是无返回值
         * 3.thenApplyAsync: 能接收上一步的返回结果,有返回值
         *
         **/
//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果: " + i);
//            return i;
//        }, newFixedThreadPool).thenApplyAsync(res -> {
//            System.out.println("任务二启动了------" + res);
//            return "hello" + res;
//        }, newFixedThreadPool);

        /**
         * 1.runAfterBothAsync: 执行完任务一,二 执行方法体,但是不能感知结果,没有返回值
         * 2.thenAcceptBothAsync: 执行完任务一,二 执行方法体,可以感知前两个方法的返回值,此方法没有返回值
         * 3.
         **/
//        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务1线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("任务1线程运行结果: " + i);
//            return i;
//        }, newFixedThreadPool);
//
//        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务二线程:" + Thread.currentThread().getId());
//            System.out.println("任务二线程结束");
//            return "Hello";
//        }, newFixedThreadPool);

//        future01.runAfterBothAsync(future02,() -> {
//            System.out.println("任务3启动");
//        },newFixedThreadPool);

//        future01.thenAcceptBothAsync(future02,(f1,f2) -> {
//            System.out.println("任务3启动" + f1 + " ---------------" + f2);
//        },newFixedThreadPool);

//        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
//            return f1 + " ---------------- " + f2;
//        }, newFixedThreadPool);

        /**
         * 两个任务,只要有一个完成,就执行任务
         * runAfterEitherAsync: 不感知结果,无返回值
         * acceptEitherAsync: 可以感知结果,但无返回值
         * applyToEitherAsync: 可以感知结果,并且有返回数据
         **/
//        future01.runAfterEitherAsync(future02,() -> {
//            System.out.println("任务3启动");
//        },newFixedThreadPool);

//        future01.acceptEitherAsync(future02,(f1)->{
//            System.out.println("任务3启动" + "---------" + f1);
//        },newFixedThreadPool);

//        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
//            return res.toString();
//        }, newFixedThreadPool);

        /**
         *
         **/

        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        },newFixedThreadPool);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "black 256g";
        },newFixedThreadPool);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的介绍");
            return "华为";
        },newFixedThreadPool);

        //CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
        //allOf.get();//等待所有结果完成
        System.out.println(anyOf.get());

        //System.out.println("main-----end" + future.get());
        /**
         * future.get() : 阻塞方法,获取返回值
         **/
    }

    public void thread(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-----start");
        /**
         * 1.继承Thread
         *      Thread01 thread01 = new Thread01();
         *      thread01.start();
         * 2.实现Runable接口
         *      Runable01 runable01 = new Runable01();
         *      new Thread(runable01).start();
         * 3.实现Callable接口 + FutureTask (可以拿到返回结果,可以处理异常)
         *         //FutureTask 底层还是Runnable
         *         FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable01());
         *         new Thread(integerFutureTask).start();
         *         //阻塞 等待整个线程完成获取返回结果
         *         Integer integer = integerFutureTask.get();
         * 4.线程池(ExecutorService)
         *      给线程池 直接提交任务
         *          newFixedThreadPool.execute(new Runable01());
         *      1.创建
         *          1.Executors
         *          2.
         *
         * 区别
         *      1,2 不能得到返回值,3可以获取返回值
         *      1,2,3都不能控制资源
         *      4 可以控制资源,也可以获取到返回值
         *        系统性能也会得到稳定
         *
         **/

        //以后在业务代码里面,以上三种启动线程的方式都不用; 存在资源浪费,将所有的多线程异步任务都交给线程池执行
        //new Thread(() -> System.out.println("hello")).start();

        //submit 可以获取到返回值 execute 无返回值
        /**
         * 原生线程池七大参数
         *   corePoolSize:[5]核心线程数[线程池不销毁,一直存在,除非设置了允许线程超时(allowCoreThreadTimeOut)];线程池,创建好以后就准备就绪的线程数量,就等待接受异步任务去执行
         *      5个 Thread thread = new Thread();
         *   maximumPoolSize:最大线程数量; 控制资源并发
         *   keepAliveTime: 存活时间.如果当前正在运行的线程数量,大于核心数量,就会按照设置时间,释放空闲线程.
         *                  释放的线程=(maximumPoolSize - corePoolSize)
         *   unit: 时间单位
         *   BlockingQueue<Runnable> workQueue: 阻塞队列.如果任务很多,并发,就会将没被线程处理的任务放置到队列,只要有线程空闲,就会去队列里面执行新的任务
         *   threadFactory: 线程的创建工厂
         *   handler: 如果workQueue队列满了,存不了更多任务,按照我们指定的拒绝策略,拒绝执行任务
         *            1.DiscardOldestPolicy: 按照时间丢弃老的任务
         *            2.CallerRunsPolicy: 同步调用,直接调用run方法
         *            3.AbortPolicy: 丢弃新来的任务 抛出异常
         *            4.DiscardPolicy: 丢弃新的任务 不抛异常
         *
         *   工作顺序:
         *      1.线程池创建,准备好core数量的核心线程数,准备接受任务
         *      2.新的任务进来,用core准备好的空闲线程执行任务
         *          2.1.core满了,就将再进来的任务放入到阻塞队列中,空闲的core就会自己去阻塞队列获取任务执行
         *          2.2 阻塞队列满了,就直接开新线程执行,最大只能开到maximumPoolSize指定的数量
         *              new LinkedBlockingDeque<>(100000)
         *                  阻塞队列,默认是Integer的最大值.可能会导致内存不够
         *                  可以传入指定参数 设置大小
         *          2.3 线程创建数量满足任务,则新创建这部分空闲线程线程(max - core)会在keepAliveTime时间后自动销毁,直到保持到core大小
         *          2.4 如果线程创建数量达到了max数量,还有新的任务进入队列,就会使用reject指定的策略,拒绝策略进行处理
         *      3. 所有线程创建都是由指定的threadFactory(线程工厂创建的)
         *
         *  面试:
         *      一个线程池,core:7,max:20,queue:50 100并发进来是怎么分配的
         *
         *     7个会立即执行(core) 100 - 7
         *     更多任务会进入队列(queue) 93 - 50
         *     再开13个线程 43 - 13
         *     剩下30个 使用拒绝策略
         *     如果拒绝策略不抛弃 直接使用CallerRunsPolicy同步调用
         *
         *
         **/
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //缓存线程池 可以执行线程工厂
        //ExecutorService executorService = Executors.newCachedThreadPool();
        //ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        //ExecutorService executorService = Executors.newSingleThreadExecutor();


        System.out.println("main-----end");
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果: " + i);
        }
    }

    public static class Runable01 implements Runnable{
        @Override
        public void run() {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果: " + i);
        }
    }

    public static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果: " + i);
            return i;
        }
    }
}
